from math import floor
import csv
import re
from datetime import datetime
from django.http import HttpResponse, HttpResponseRedirect
from fiteat.forms import CSVMapForm
from django.shortcuts import render_to_response
from itertools import count
from fiteat.middleware import threadlocals
from datetime import timedelta
from django.utils.translation import ugettext as _

DAYS_FOR_AVERAGE = 7

def get_units(prefs):
    system = prefs.units_system
    if system == 'M':
        weight = _('kg')
        length = _('cm')
        distance = _('km')
    else:
        weight = _('lbs')
        length = _('in')
        distance = _('m')
    return weight, length, distance

def wma(n, vals):
    wma = []
    total = 0
    numerator = 0
    denominator = 0
    for m, val in enumerate(vals):
        new_total = total + val
        if m >= n:
            new_total -= vals[m-n]
        if m < n:
            denominator += n - m
        numerator += n * val - total
        wma.append(numerator/denominator)
        total = new_total
    return wma

def truncate(num):
    return round(num, 2)

def flatten(l):
    """ An ugly flattening code for a case of [(x, y), (a, b)] """
    ll = []
    for x in l:
        ll += [x[0]]
        ll += [x[1]]
    return ll

def export_func(request, fields, data, filename):
    """
    A generic export view. Receives the request object, the list of fields in the export,
    and the data to export.
    """
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=%s' % filename

    writer = csv.writer(response)
    writer.writerow(fields)

    for item in data:
        line = []
        for field in fields:
            line.append(getattr(item, field))
        writer.writerow(line)

    return response

def import_func(request, import_template, map_fields_template, field_choices):
    """ 
    A generic import view.
    If a file has been uploaded - proceed to mapping of fields.
    Otherwise - present a page for upload
    """
    if 'import_file' not in request.FILES:
        return render_to_response(import_template)

    file = request.FILES['import_file']['content']
    request.session['import_file'] = file
    csv_reader = csv.reader(file.splitlines())
    headers = csv_reader.next()
    form = CSVMapForm(zip(headers, count()), field_choices)
    return render_to_response(map_fields_template, {'form': form.as_table()})

def map_fields_func(request, map_fields_template, field_choices, data_class, model_name, redirect_url):
    """ 
    At this point we should have a CSV file as part of the session, and
    a filled form as part of the request
    """
    file = request.session['import_file']
    csv_reader = csv.reader(file.splitlines())
    headers = csv_reader.next()
    """
    the form has the fields named id_n where n is the number of the field
    in the CSV file, and the value is the name of the field in the DB
    mapped into from the CSV field
    """
    form = CSVMapForm(zip(headers, count()), field_choices, request.POST)
    if not form.is_valid():
        return render_to_response(map_fields_template, {'form': form.as_table()})
    field_map = dict()
    """ Build a map of fields """
    for field_id, field_val in form.cleaned_data.iteritems():
        id = re.sub('id_', '', field_id)
        field_map[int(id)] = field_val
    duplicates = False
    for i, fields in enumerate(csv_reader):
        data_item = data_class()
        for field_num, field_name in field_map.iteritems():
            val = fields[field_num]
            if field_name == 'date':
                """ An ugly hack - determining the date format """
                if re.match('\d{4}-\d{1,2}-\d{1,2}', val):
                    val = datetime.strptime(val, '%Y-%m-%d').date()
                elif re.match('\d{1,2}/\d{1,2}/\d{4}', val):
                    val = datetime.strptime(val, '%d/%m/%Y').date()
                elif re.match('\d{1,2}-\w{3}-\d{4}', val):
                    val = datetime.strptime(val, '%d-%b-%Y').date()
            if val == '':
                val = None
            data_item.__setattr__(field_name, val)
        try:
            data_item.save()
        except Exception, ie:
            request.user.message_set.create(message="Your %(name)s where successfully imported up to #%(num)d. It came up with the following error: %(err)s" % ({'name': model_name, 'num': i, 'err': ie}))
            return HttpResponseRedirect(redirect_url)
    message = _("Your %s file has been successfully imported" % model_name)
    request.user.message_set.create(message=message)
    return HttpResponseRedirect(redirect_url)

def save_with_trends(self, model, fields):
    self.user = threadlocals.get_current_user()
    items = model.objects.filter(date__gt=(self.date - timedelta(DAYS_FOR_AVERAGE))).filter(date__lt=(self.date + timedelta(DAYS_FOR_AVERAGE)))
    denominator = dict()
    numerator = dict()
    for field in fields:
        denominator[field] = 0
        numerator[field] = 0
    for m in items:
        if m.date <= self.date:
            days_delta = (self.date - m.date).days
            for field in fields:
                val = getattr(m, field)
                if val:
                    numerator[field] += (DAYS_FOR_AVERAGE - days_delta) * val
                    denominator[field] += (DAYS_FOR_AVERAGE - days_delta)
# We assume here that nobody in his right mind will want to fix an old weight value
# this assumption may prove to be false in the future.
    for field in fields:
        if denominator[field] != 0:
            setattr(self, '%s_trend' % field, truncate(numerator[field] / denominator[field]))
        else:
            setattr(self, '%s_trend' % field, getattr(self, field))

    super(model,self).save()

class K:
    def __init__(self, label=None, **kwargs):
        assert(len(kwargs) == 1)
        for k, v in kwargs.items():
            self.id = k
            self.v = v
        self.label = label or self.id

class Constants:
    def __init__(self, *args):
        self.klist = args
        for k in self.klist:
            setattr(self, k.id, k.v)

    def choices(self):
        return [(k.v, k.label) for k in self.klist]


